/*
 * process.c
 *
 *  Created on: Mar 10, 2012
 *      Author: Dan
 */
#include<stdio.h>
#include "process.h"
#include "process_type.h"
#include"../Utility/instruction.h"
#include "../Shared Memory/memory.h"
#include <stdlib.h>
#include <time.h>
#include "../CPU/interrupt.h"
int DEBUG = 1;

/** Private Create Functions**/

// Has this Location been used already?  (1 True=Is done / 0 False=Is Not done)
int isdone(int loc, int* arr){
	int i;
	for (i = 0; i < sizeof(arr); i++){
		if (arr[i] == loc) {return 0;}
	}
	return 1;
}

int gethigher(int value, int max){
	while(1){
		int newval = rand() % max;
		if (newval > value) {return newval;}
	}
}

void printdebug(process* p){

	if(DEBUG){
		int i;
		// Debug Print Statements
		char* name;
		switch(p->type){
		case NUMBER_CRUNCHER:
			name = "Number Cruncher";
			break;
		case CONSUMER:
			name = "Consumer";
			break;
		case PRODUCER:
			name = "Producer";
			break;
		case USER_INTERFACE:
			name = "User Interface";
			break;
		}
		for (i = 0; i < p->no_steps; i++)
		{
			int addr;
			char* opcode;
			switch((p->requests[i])->operation){
			case NO_OP:
				continue;
				break;
			case DEC_MEMORY:
				opcode = "Decrement Memory";
				break;
			case INC_MEMORY:
				opcode = "Increment Memory";
				break;
			case INPUT_WAIT:
				if ((p->requests[i])->address == KEYBOARD){
					opcode = "KB Input Wait";
				} else {
					opcode = "Aux IO Wait";}
			default:
				printf("Default %d", (p->requests[i])->operation);
				break;
			}
			addr = (p->requests[i])->address;
			printf("Process %s Addr: %d Opcode %s\n", name, addr, opcode);
		}
		// END Debug Print Statements
	}
}

void flood_noops(process* p){
	int i;
	for (i = 0; i < p->no_steps; i++){
		instruction* instr = instruction_init(NO_OP, i); // Since a NoOp does nothing, using an address as a counter is safe.
		p->requests[i] = instr;
	}
}

void create_user_interface(process* p){
	// Flood entire array with no op instrcutions
	flood_noops(p);

	// Pick Some Waits for User Input
	int i = 0;
	for (i = 0; i < p->no_requests; i++){
		int instraddress = rand() % (p->no_steps - 1);
		instruction* instr = instruction_init(INPUT_WAIT, KEYBOARD);
		(p->requests)[instraddress] = instr;
	}
	printdebug(p);
}


void create_consumer_producer(process* p){
	// Flood entire array with no op instructions
	flood_noops(p);
	// Use the Official Memory Address Array
	int* address = p->address;
	int i;

	if(DEBUG){
		for (i = 0; i < p->no_requests; i++){
			printf("Official Memory %d\n", address[i]);
		}
	}

	// For Loop - to pick X Random Memory Addresses and
	// For Loop - Pick X Random Pairs of Instructions

	int* memoryaddr = (int*)malloc(sizeof(int)* p->no_requests);
	int* instrs = (int*)malloc(sizeof(int)* p->no_requests); // Instruction address array
	for (i =0; i < p->no_requests; i++){
		// Address to use in Memory From Official Address Array
		int memaddress = address[i];
		memoryaddr[i] = memaddress;

		int instraddress = rand() % (p->no_steps - 1);
		instrs[i] = instraddress;

	}

	// Now we have 3 SAFE Arrays.   The index of each all correspond to a "Tuple" for write/free/semaphore and memory address
	// With that, we can build our instructions.
	for (i =0; i < p->no_requests; i++){
		// Memory Address Shared between ALL actions.
		int memaddress = memoryaddr[i];
		int instraddress = instrs[i];;
		instruction* instr;
		switch(p->type){
			case PRODUCER:
				// Write Instruction i = Allocate Memory @ address Producer ONLY
				instr = instruction_init(INC_MEMORY, memaddress);
				(p->requests)[instraddress] = instr;
				break;
			case CONSUMER:
				// Free Instruction i = Free Memory @ address Consumer ONLY
				instr = instruction_init(DEC_MEMORY, memaddress);
				(p->requests)[instraddress] = instr;
				break;
			}
	}
	printdebug(p);
}



void create_number_cruncher(process* p){
	// Flood Array with No Ops
	flood_noops(p);
	// For Loop - to pick X Random Memory Addresses and
	// For Loop - Pick X Random Pairs of Instructions
	int i;
	int* memoryaddr = (int*)malloc(sizeof(int)* p->no_requests);
	int* wrinstrs = (int*)malloc(sizeof(int)* p->no_requests); // Write Instruction address array
	int* frinstrs = (int*)malloc(sizeof(int)* p->no_requests); // Free Instruction address array
	for (i =0; i < p->no_requests; i++){
		// Address to use in Memory
		int done = 0;
		int memaddress;
		while(!done){  // Don't use Duplicate Memory Addresses
			memaddress = rand() % (p->memory_size - 1);
			done = isdone(memaddress, memoryaddr);
		}
		memoryaddr[i] = memaddress;

		// Instruction to Write to Memory at
		int instraddress;
		done = 0;
		while(!done){ // Don't use duplicate Instruction Addresses from EITHER Write Instructions OR Free Instructions
			instraddress = rand() % (p->no_steps / 2 - 1);
			done = isdone(instraddress, wrinstrs) & isdone(instraddress, frinstrs);
		}
		wrinstrs[i] = instraddress;

		// Instruction to Free to Memory At (> write address)
		done = 0;
		int frinstraddress;
		while (!done){  // Also don't use duplicate instruction address...
			frinstraddress = gethigher(instraddress, p->no_steps - 1);
			done = isdone(frinstraddress, wrinstrs) & isdone(frinstraddress, frinstrs);
		}
		frinstrs[i] = frinstraddress;
	}

	// Now we have 3 SAFE Arrays.   The index of each all correspond to a "Tuple" for write/free and memory address
	// With that, we can build our instructions.
	for (i =0; i < p->no_requests; i++){
		// Memory Address
		int memaddress = memoryaddr[i];

		// Write Instruction i = Allocate Memory @ address
		int instraddress = wrinstrs[i];
		instruction* mem_alloc = instruction_init(DEC_MEMORY, memaddress);
		(p->requests)[instraddress] = mem_alloc;

		// Free Instruction i = Free Memory @ address
		instraddress = frinstrs[i];
		instruction* mem_dalloc = instruction_init(INC_MEMORY, memaddress);
		(p->requests)[instraddress] = mem_dalloc;
	}
	printdebug(p);
}
/** END Private Create Functions**/

instruction* get_instruction(process* p, int pc){
	return (p->requests[pc]);
}

int get_pc(process* p){
	return p->pcb.next_step;
}

void save_pc(process* p, int pc){
	p->pcb.next_step = pc;
}

process* process_init(int num_steps, int num_requests, int the_type, int the_id, int* the_address, int the_mem_size){

	process* p = (process*)malloc(sizeof(process)*num_steps);
	p->no_steps = num_steps;
	p->no_requests = num_requests;
	p->type = the_type;
	p->address = the_address;
	p->pcb.process_id = the_id;
	p->pcb.next_step = 0;
	p->pcb.state = RUNNABLE;
	p->pcb.waiting_on = -1;
	p->memory_size = the_mem_size;

	p->requests =  malloc(sizeof(instruction) * (num_steps -1));

	p->getpc = &get_pc;
	p->getinstruction = &get_instruction;
	p->savepc = &save_pc;

	char* name = "";
	switch(p->type){
	case NUMBER_CRUNCHER:
		name = "Number Cruncher";
		create_number_cruncher(p);
		break;
	case CONSUMER:
		name = "Consumer";
		create_consumer_producer(p);
		break;
	case PRODUCER:
		name = "Producer";
		create_consumer_producer(p);
		break;
	case USER_INTERFACE:
		name = "User Interface";
		create_user_interface(p);
	}

	p->name = name;
	return  p;
}


